vue@3学习
本文最后更新于:2022年5月10日 下午
VUE3带来的好东西
Proxy API
vue2.X存在的缺陷
- 无法检测对象中
property
的添加或移除; - 通过下标操作数组不会触发响应 ,
vm.items[indexOfItem] = newValue
能做到但性能考虑放弃; - 修改数组的长度时不会触发响应式,
vm.items.length = newLength
; - vue2时代的解决方法
// Object新增元素 Vue.set(object, propertyName, value) // or this.$set(this.someObject,'b',2) // 下标修改数组 Vue.set(vm.array1, indexOfItem, newValue) // or this.$set(this.array1, indexOfItem, newValue) // or this.array1.splice(indexOfItem, 1, newValue) // 修改数组长度 this.array1.splice(newLength)
- 重写了数组部分方法以支持响应式
pop() push() shift() unshift() splice() sort() reverse()
defineProperty
Object.defineProperty(obj/* 对象*/, prop/* 属性*/, descriptor/*描述符*/){
//对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
//数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
//存取描述符是由 getter 函数和 setter 函数所描述的属性。
//一个描述符只能是这两者其中之一;不能同时是两者。
configurable: true,
//configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。
enumerable: true,
//enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
value: true,
writable: true,
//当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。
//
// 或者
//
get() { ... },
set(newValue) { ... },
enumerable : true,
configurable : true
}
- 兼容
IE9
- 劫持了对象上的
属性
- 数据结构越复杂初始性能会越差,需要递归遍历绑定
defineProperty
- 为保持响应式还需要为新增数据再次绑定
defineProperty
- 需要对原始数据修改来触发拦截器
proxy
const p = new Proxy(target, handler)
handler.getPrototypeOf()
//Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf()
//Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible()
//Object.isExtensible 方法的捕捉器。
handler.preventExtensions()
//Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor()
//Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.defineProperty()
//Object.defineProperty 方法的捕捉器。
handler.has()
//in 操作符的捕捉器。
handler.get()
//属性读取操作的捕捉器。
handler.set()
//属性设置操作的捕捉器。
handler.deleteProperty()
//delete 操作符的捕捉器。
handler.ownKeys()
//Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
handler.apply()
//函数调用操作的捕捉器。
handler.construct()
//new 操作符的捕捉器。
- 不兼容
IE
- 劫持
整个对象
,无需对对象本体作出修改 - 响应数组的改变
- 拦截器响应的是
proxy
生成的代理对象,原数据的修改将不会触发拦截器 - 拦截器种类繁多
组合式API生命周期
onXXXX
beforeCreate + created => setup
beforeMount => onBeforeMount
mounted => onMounted
beforeUpdate => onBeforeUpdate
updated => onUpdated
beforeDestroy => onBeforeUnmount
destroyed => onUnmounted
// keep-live组件
activated => onActivated
deactivated => onDeactivated
// 错误捕获
oneErrorCaptured
// 追踪
onRenderTracked
onRenderTriggered
Composition API
组合式api,解决关注点分离问题,同一逻辑代码可以摆放在一起
Hooks
css in js
自定义指令
自定钩子与组件生命周期统一
created() // 在元素的 attribute 或事件监听器被应用之前调用。
beforeMount()
mounted()
beforeUpdate()
updated()
beforeUnmount()
unmounted()
const app = Vue.createApp({})
app.directive('highlight', {
beforeMount(el, binding, vnode) {
el.style.background = binding.value
}
})
简单实现
实现reactive
// reactivity.js 猴版响应式框架
const targetMap = new WeakMap();
const effectStack = [];
const isObject = obj => obj !== null && typeof obj === "object";
const baseHandler = {
get(target, key) {
const ret = Reflect.get(target, key);
track(target, key);
if (typeof ret === "object") {
return reactive(ret);
}
return ret;
},
set(target, key, val) {
Reflect.set(target, key, val);
trigger(target, key, val);
},
};
function track(target, key) {
let activeEffect = effectStack[effectStack.length - 1];
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
// TODO
activeEffect.deps.push(dep);
}
}
function trigger(target, key, val) {
const depsMap = targetMap.get(target);
if (!depsMap) {
// never been tracked
return;
}
const effects = new Set();
if (key) {
const dep = depsMap.get(key);
dep.forEach((effect) => {
effects.add(effect);
});
}
effects.forEach((ef) => ef());
}
function createReactiveEffect(fn,options){
const effect = function reactiveEffect(){
try {
effectStack.push(effect)
return fn()
} finally {
effectStack.pop()
}
}
effect.deps = []
effect.options = options
return effect
}
// ---------
function reactive(target) {
return new Proxy(target, baseHandler);
}
function effect(fn, options={}) {
const effect = createReactiveEffect(fn, options);
if (!options.lazy) {
effect();
}
return effect;
}
function computed(fn){
const runner = effect(fn,{computed:true,lazy:true})
return {
effect:runner,
get val(){
return runner()
}
}
}
简单实现vuex
import {reactive,inject,provide} from 'vue'
const STORE_KEY = Symbol()
function useStore(){
return reject(STORE_KEY)
}
function createStore(options){
return new Store(options)
}
class Store{
constructor(options){
const {state,mutations} = options
this._state = reactive({
data:state
})
this._mutations = mutations
}
get state(){
return this._state.data
}
commit(type,payload){
const entry = this._mutations[type]
entry && entry(this.state,payload)
}
install(app){
app.provide(STORE_KEY,this)
}
}
export {createStore,useStore}
vue@3学习
http://yoursite.com/2022/02/24/[源码]vue3/